﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GameDataTempshitEditor
{

    public partial class Builder : Form
    {
        public Builder()
        {
            InitializeComponent();

            backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
            progressBar1.Maximum = 1000;
        }

        void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;

            var txt = e.UserState as string;
            if (txt != null)
                label2.Text = txt;
        }

        public void Start(string cookerPath, string outputPath, string reflectionPath, string dataPath, bool verbose)
        {
            if (!backgroundWorker1.IsBusy)
            {
                CookerPath = cookerPath;
                OutputPath = outputPath;
                ReflectionPath = reflectionPath;
                DataPath = dataPath;
                Verbose = verbose;

                label2.Text = "Starting process...";
                progressBar1.Value = 0;

                richTextBox1.Text = "Starting process..." + Environment.NewLine;
                richTextBox2.Text = "";

                backgroundWorker1.RunWorkerAsync();
            }
        }

        // set externally
        private string CookerPath;
        private string OutputPath;
        private string ReflectionPath;
        private string DataPath;
        private bool Verbose;

        private void button2_Click(object sender, EventArgs e)
        {
            if (!backgroundWorker1.CancellationPending)
            {
                backgroundWorker1.CancelAsync();
                button3.Enabled = false; // cancel button
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Hide();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                Process process = new Process();

                // Configure the process using the StartInfo properties.
                process.StartInfo.FileName = CookerPath;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.UseShellExecute = false;

                // format argument list
                process.StartInfo.Arguments = "";
                if (Verbose)
                    process.StartInfo.Arguments += "-verbose ";
                process.StartInfo.Arguments += "-projects=" + ReflectionPath;
                process.StartInfo.Arguments += " -data=" + DataPath;
                process.StartInfo.Arguments += " -out=" + OutputPath;

                // bind hooks
                process.OutputDataReceived += process_OutputDataReceived;
                process.ErrorDataReceived += process_OutputDataReceived;

                // run the process
                if (!process.Start())
                {
                    backgroundWorker1.ReportProgress(0, "Error: Failed to run application");
                    return;
                }
                
                // capture input
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();

                // allow the process to be canceled
                button3.Enabled = true;

                // wait for the process to finish
                while (!process.WaitForExit(500))
                {
                    if (backgroundWorker1.CancellationPending)
                    {
                        AddToLog_Thread("Received cancel request" + Environment.NewLine);
                        
                        process.Close();
                        process.Dispose();

                        backgroundWorker1.ReportProgress(0, "Error: Process killed");
                        AddToLog_Thread("Process killed" + Environment.NewLine);
                    }
                }

                // flush the input
                Thread.Sleep(200);
                process.CancelOutputRead();
                process.CancelErrorRead();

                // finished, didn't crash check error code
                if (process.ExitCode == 0)
                {
                    AddToLog_Thread(Environment.NewLine);
                    AddToLog_Thread("Process finished, no errors" + Environment.NewLine);
                    backgroundWorker1.ReportProgress(1000, "Process finished");
                }
                else
                {
                    AddToLog_Thread(Environment.NewLine);
                    AddToLog_Thread("Error: Process failed, exit code = " + process.ExitCode + " " + Environment.NewLine);
                    backgroundWorker1.ReportProgress(1000, "Process failed");
                }
            }
            catch (Exception exc)
            {
                var str = exc.ToString();
                AddToLog_Thread("Error: Fatal exception: " + str + Environment.NewLine);
            }
        }

        void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Data))
                return;

            string txt = (string)e.Data;

            // progress tracking
            if (txt.StartsWith("[Progress]"))
            {
                var p = txt.Substring(10).Split(',');

                if (p.Length >= 2)
                {
                    int val = 0;
                    if (int.TryParse(p[0], out val))
                    {
                        backgroundWorker1.ReportProgress(val, p[1]);
                    }
                }

                return;
            }

            // logging
            AddToLog_Thread(txt + Environment.NewLine);
        }

        void AddToLog_Thread(string txt)
        {
            this.Invoke((MethodInvoker)delegate { AddToLog(txt); });

        }

        void AddToLog(string txt)
        {
            var isVerbose = txt.StartsWith("Verbose:");
            if (isVerbose)
            {
                richTextBox2.AppendText(txt.Substring(8).TrimStart());
            }
            else
            {
                var isError = txt.StartsWith("Error:") || txt.StartsWith("error:");

                if (isError)
                {
                    richTextBox1.SelectionStart = richTextBox1.TextLength;
                    richTextBox1.SelectionLength = 0;

                    richTextBox1.SelectionColor = isError ? Color.Red : Color.Black;
                    richTextBox1.AppendText(txt);

                    richTextBox1.SelectionColor = richTextBox1.ForeColor;
                }
                else
                {
                    richTextBox1.AppendText(txt);
                }
            }
        }

        private void Builder_FormClosing(object sender, FormClosingEventArgs e)
        {
            Hide();
            e.Cancel = true;
        }

        private void tabPage2_Click(object sender, EventArgs e)
        {

        }
    }        
}
